package com.softwareengineering.trafficapplicationv2;

/**
 * This file is part of the Traffic Simulation Application.
 * The Traffic Simulation Application is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * The Traffic Simulation Application is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with the Traffic Simulation Application.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * @file IDM.java
 * @author (c) Martin Treiber, http://traffic-simulation.de/
 * @alterations David Black, Chris Nelson, and Garrett Stibbs, (c) December 03, 2012
 * @description Basis class for the microscopic traffic model IDM [intelligent-driver model,
 * 				see "http://xxx.uni-augsburg.de/abs/cond-mat/0002177"
 * 				M. Treiber, A. Hennecke, and D. Helbing, Congested Traffic States in Empirical
 * 				Observations and Microscopic Simulations, Phys. Rev. E 62, 1805 (2000)].
 * @note The classes IDMCar, IDMTruck, etc are concrete realizations of this class for trucks, cars, etc. 
 * 
 */

public abstract class IDM implements MicroModel {

	private static final int ismax=100;	// ve(s)=ve(ismax) for s>ismax assumed
	public double a;
	public double b;
	public double delta;
	public double s0;
	public double s1;
	public double sqrtab;
	public double T;
	public double v0;
	private double[] veqTab = new double[ismax+1];	// table in steps of ds=1m

	public IDM(){; }

	public double calcAcc(Moveable bwd, Moveable vwd){
		double delta_v=bwd.velocity()-vwd.velocity();
		double s=vwd.position()-bwd.position()-bwd.length();  // pos: END of vehicles!
		double vel = bwd.velocity();
		double s_star_raw = s0 + s1*Math.sqrt(vel/v0)+vel*T
				+ (vel*delta_v)/(2*sqrtab);
		double s_star = (s_star_raw > s0) ? s_star_raw : s0;
		double acc = a * (1 - Math.pow((vel/v0),delta) -(s_star*s_star)/(s*s) );
		
		if (acc < -Constants.MAX_BRAKING)
			acc = -Constants.MAX_BRAKING;
		
		return acc;	
	}
	
	public void initialize(){
		final double dt=0.5;	// relaxation with timestep=0.5 s
		final double kmax=20;	// number of iterations in rlaxation
		veqTab[0]=0.0;
		
		for (int is=1; is<=ismax ; is++){	// table in steps of ds=1 (m)

			double Ve=veqTab[is-1];

			for (int k=0; k<kmax; k++){
				double s_star = s0 + s1*Math.sqrt(Ve/v0)+Ve*T;	    
				double acc=
						a * (1 - Math.pow((Ve/v0),delta) - (s_star*s_star)/(is*is) );
				Ve=Ve+acc*dt;
				
				if (Ve<0.0) Ve=0.0;
			}
			
			veqTab[is]=Ve;
		}
	}
	
	public void set_a(double a){this.a=a;}
	
	public void set_b(double b){this.b=b;}
	
	public void set_params(IDM idm){
		this.set_v0(idm.v0);
		this.set_a(idm.a);
		this.set_b(idm.b);
		this.set_T(idm.T);
		this.set_s0(idm.s0);
		this.set_s1(idm.s1);
	}
	public void set_s0(double s0){this.s0=s0;}

	public void set_s1(double s1){this.s1=s1;}

	public void set_T(double T){this.T=T;}

	public void set_v0(double v0){this.v0=v0;}

	public double Veq(double dx){
		int is = (int) dx;
		double V=0.0;
		
		if (is<ismax){
			double rest=dx-((double) is);
			V = (1-rest)*veqTab[is] + rest*veqTab[is+1];
		}
		
		if (is>=ismax) V=veqTab[ismax];
		
		if (is<=0) V=0.0;
		
		return V;
	}

}
